/*
 * Copyright (C) 2005 - 2013 MaNGOS <http://www.getmangos.com/>
 *
 * Copyright (C) 2008 - 2013 Trinity <http://www.trinitycore.org/>
 *
 * Copyright (C) 2010 - 2013 ArkCORE <http://www.arkania.net/>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 */

#ifndef ARKCORE_GRIDNOTIFIERS_H
#define ARKCORE_GRIDNOTIFIERS_H

#include "ObjectGridLoader.h"
#include "UpdateData.h"
#include <iostream>

#include "Corpse.h"
#include "Object.h"
#include "DynamicObject.h"
#include "GameObject.h"
#include "Player.h"
#include "Unit.h"
#include "CreatureAI.h"

class Player;
//class Map;

namespace Trinity
{
    struct VisibleNotifier
    {
        Player &i_player;
        UpdateData i_data;
        std::set<Unit*> i_visibleNow;
        Player::ClientGUIDs vis_guids;

        VisibleNotifier (Player &player) :
                i_player(player), vis_guids(player.m_clientGUIDs)
        {
            i_data.m_map = uint16(player.GetMapId());
        }
        template<class T> void Visit (GridRefManager<T> &m);
        void SendToSelf (void);
    };

    struct VisibleChangesNotifier
    {
        WorldObject &i_object;

        explicit VisibleChangesNotifier (WorldObject &object) :
                i_object(object)
        {
        }
        template<class T> void Visit (GridRefManager<T> &)
        {
        }
        void Visit (PlayerMapType &);
        void Visit (CreatureMapType &);
        void Visit (DynamicObjectMapType &);
    };

    struct PlayerRelocationNotifier: public VisibleNotifier
    {
        PlayerRelocationNotifier (Player &pl) :
                VisibleNotifier(pl)
        {
        }

        template<class T> void Visit (GridRefManager<T> &m)
        {
            VisibleNotifier::Visit(m);
        }
        void Visit (CreatureMapType &);
        void Visit (PlayerMapType &);
    };

    struct CreatureRelocationNotifier
    {
        Creature &i_creature;
        CreatureRelocationNotifier (Creature &c) :
                i_creature(c)
        {
        }
        template<class T> void Visit (GridRefManager<T> &)
        {
        }
        void Visit (CreatureMapType &);
        void Visit (PlayerMapType &);
    };

    struct DelayedUnitRelocation
    {
        Map &i_map;
        Cell &cell;
        CellPair &p;
        const float i_radius;
        DelayedUnitRelocation (Cell &c, CellPair &pair, Map &map, float radius) :
                i_map(map), cell(c), p(pair), i_radius(radius)
        {
        }
        template<class T> void Visit (GridRefManager<T> &)
        {
        }
        void Visit (CreatureMapType &);
        void Visit (PlayerMapType &);
    };

    struct AIRelocationNotifier
    {
        Unit &i_unit;
        bool isCreature;
        explicit AIRelocationNotifier (Unit &unit) :
                i_unit(unit), isCreature(unit.GetTypeId() == TYPEID_UNIT)
        {
        }
        template<class T> void Visit (GridRefManager<T> &)
        {
        }
        void Visit (CreatureMapType &);
    };

    struct GridUpdater
    {
        GridType &i_grid;
        uint32 i_timeDiff;
        GridUpdater (GridType &grid, uint32 diff) :
                i_grid(grid), i_timeDiff(diff)
        {
        }

        template<class T> void updateObjects (GridRefManager<T> &m)
        {
            for (typename GridRefManager<T>::iterator iter = m.begin(); iter != m.end(); ++iter)
                iter->getSource()->Update(i_timeDiff);
        }

        void Visit (PlayerMapType &m)
        {
            updateObjects<Player>(m);
        }
        void Visit (CreatureMapType &m)
        {
            updateObjects<Creature>(m);
        }
        void Visit (GameObjectMapType &m)
        {
            updateObjects<GameObject>(m);
        }
        void Visit (DynamicObjectMapType &m)
        {
            updateObjects<DynamicObject>(m);
        }
        void Visit (CorpseMapType &m)
        {
            updateObjects<Corpse>(m);
        }
    };

    struct MessageDistDeliverer
    {
        WorldObject *i_source;
        WorldPacket *i_message;
        uint32 i_phaseMask;
        float i_distSq;
        uint32 team;
        Player const* skipped_receiver;
        MessageDistDeliverer (WorldObject *src, WorldPacket *msg, float dist, bool own_team_only = false, Player const* skipped = NULL) :
                i_source(src), i_message(msg), i_phaseMask(src->GetPhaseMask()), i_distSq(dist * dist), team((own_team_only && src->GetTypeId() == TYPEID_PLAYER) ? ((Player*) src)->GetTeam() : 0), skipped_receiver(skipped)
        {
        }
        void Visit (PlayerMapType &m);
        void Visit (CreatureMapType &m);
        void Visit (DynamicObjectMapType &m);
        template<class SKIP> void Visit (GridRefManager<SKIP> &)
        {
        }

        void SendPacket (Player* plr)
        {
            // never send packet to self
            if (plr == i_source || (team && plr->GetTeam() != team) || skipped_receiver == plr)
                return;

            if (!plr->HaveAtClient(i_source))
                return;

            if (WorldSession* session = plr->GetSession())
                session->SendPacket(i_message);
        }
    };

    struct ObjectUpdater
    {
        uint32 i_timeDiff;
        explicit ObjectUpdater (const uint32 &diff) :
                i_timeDiff(diff)
        {
        }
        template<class T> void Visit (GridRefManager<T> &m);
        void Visit (PlayerMapType &)
        {
        }
        void Visit (CorpseMapType &)
        {
        }
        void Visit (CreatureMapType &);
    };

// SEARCHERS & LIST SEARCHERS & WORKERS

// WorldObject searchers & workers

    template<class Check>
    struct WorldObjectSearcher
    {
        uint32 i_phaseMask;
        WorldObject* &i_object;
        Check &i_check;

        WorldObjectSearcher (WorldObject const* searcher, WorldObject* & result, Check& check) :
                i_phaseMask(searcher->GetPhaseMask()), i_object(result), i_check(check)
        {
        }

        void Visit (GameObjectMapType &m);
        void Visit (PlayerMapType &m);
        void Visit (CreatureMapType &m);
        void Visit (CorpseMapType &m);
        void Visit (DynamicObjectMapType &m);

        template<class NOT_INTERESTED> void Visit (GridRefManager<NOT_INTERESTED> &)
        {
        }
    };

    template<class Check>
    struct WorldObjectListSearcher
    {
        uint32 i_phaseMask;
        std::list<WorldObject*> &i_objects;
        Check& i_check;

        WorldObjectListSearcher (WorldObject const* searcher, std::list<WorldObject*> &objects, Check & check) :
                i_phaseMask(searcher->GetPhaseMask()), i_objects(objects), i_check(check)
        {
        }

        void Visit (PlayerMapType &m);
        void Visit (CreatureMapType &m);
        void Visit (CorpseMapType &m);
        void Visit (GameObjectMapType &m);
        void Visit (DynamicObjectMapType &m);

        template<class NOT_INTERESTED> void Visit (GridRefManager<NOT_INTERESTED> &)
        {
        }
    };

    template<class Do>
    struct WorldObjectWorker
    {
        uint32 i_phaseMask;
        Do const& i_do;

        WorldObjectWorker (WorldObject const* searcher, Do const& _do) :
                i_phaseMask(searcher->GetPhaseMask()), i_do(_do)
        {
        }

        void Visit (GameObjectMapType &m)
        {
            for (GameObjectMapType::iterator itr = m.begin(); itr != m.end(); ++itr)
                if (itr->getSource()->InSamePhase(i_phaseMask))
                    i_do(itr->getSource());
        }

        void Visit (PlayerMapType &m)
        {
            for (PlayerMapType::iterator itr = m.begin(); itr != m.end(); ++itr)
                if (itr->getSource()->InSamePhase(i_phaseMask))
                    i_do(itr->getSource());
        }
        void Visit (CreatureMapType &m)
        {
            for (CreatureMapType::iterator itr = m.begin(); itr != m.end(); ++itr)
                if (itr->getSource()->InSamePhase(i_phaseMask))
                    i_do(itr->getSource());
        }

        void Visit (CorpseMapType &m)
        {
            for (CorpseMapType::iterator itr = m.begin(); itr != m.end(); ++itr)
                if (itr->getSource()->InSamePhase(i_phaseMask))
                    i_do(itr->getSource());
        }

        void Visit (DynamicObjectMapType &m)
        {
            for (DynamicObjectMapType::iterator itr = m.begin(); itr != m.end(); ++itr)
                if (itr->getSource()->InSamePhase(i_phaseMask))
                    i_do(itr->getSource());
        }

        template<class NOT_INTERESTED> void Visit (GridRefManager<NOT_INTERESTED> &)
        {
        }
    };

// Gameobject searchers

    template<class Check>
    struct GameObjectSearcher
    {
        uint32 i_phaseMask;
        GameObject* &i_object;
        Check &i_check;

        GameObjectSearcher (WorldObject const* searcher, GameObject* & result, Check& check) :
                i_phaseMask(searcher->GetPhaseMask()), i_object(result), i_check(check)
        {
        }

        void Visit (GameObjectMapType &m);

        template<class NOT_INTERESTED> void Visit (GridRefManager<NOT_INTERESTED> &)
        {
        }
    };

// Last accepted by Check GO if any (Check can change requirements at each call)
    template<class Check>
    struct GameObjectLastSearcher
    {
        uint32 i_phaseMask;
        GameObject* &i_object;
        Check& i_check;

        GameObjectLastSearcher (WorldObject const* searcher, GameObject* & result, Check& check) :
                i_phaseMask(searcher->GetPhaseMask()), i_object(result), i_check(check)
        {
        }

        void Visit (GameObjectMapType &m);

        template<class NOT_INTERESTED> void Visit (GridRefManager<NOT_INTERESTED> &)
        {
        }
    };

    template<class Check>
    struct GameObjectListSearcher
    {
        uint32 i_phaseMask;
        std::list<GameObject*> &i_objects;
        Check& i_check;

        GameObjectListSearcher (WorldObject const* searcher, std::list<GameObject*> &objects, Check & check) :
                i_phaseMask(searcher->GetPhaseMask()), i_objects(objects), i_check(check)
        {
        }

        void Visit (GameObjectMapType &m);

        template<class NOT_INTERESTED> void Visit (GridRefManager<NOT_INTERESTED> &)
        {
        }
    };

    template<class Functor>
    struct GameObjectWorker
    {
        GameObjectWorker (WorldObject const* searcher, Functor& func) :
                _func(func), _phaseMask(searcher->GetPhaseMask())
        {
        }

        void Visit (GameObjectMapType& m)
        {
            for (GameObjectMapType::iterator itr = m.begin(); itr != m.end(); ++itr)
                if (itr->getSource()->InSamePhase(_phaseMask))
                    _func(itr->getSource());
        }

        template<class NOT_INTERESTED> void Visit (GridRefManager<NOT_INTERESTED> &)
        {
        }

    private:
        Functor& _func;
        uint32 _phaseMask;
    };

// Unit searchers

// First accepted by Check Unit if any
    template<class Check>
    struct UnitSearcher
    {
        uint32 i_phaseMask;
        Unit* &i_object;
        Check & i_check;

        UnitSearcher (WorldObject const* searcher, Unit* & result, Check & check) :
                i_phaseMask(searcher->GetPhaseMask()), i_object(result), i_check(check)
        {
        }

        void Visit (CreatureMapType &m);
        void Visit (PlayerMapType &m);

        template<class NOT_INTERESTED> void Visit (GridRefManager<NOT_INTERESTED> &)
        {
        }
    };

// Last accepted by Check Unit if any (Check can change requirements at each call)
    template<class Check>
    struct UnitLastSearcher
    {
        uint32 i_phaseMask;
        Unit* &i_object;
        Check & i_check;

        UnitLastSearcher (WorldObject const* searcher, Unit* & result, Check & check) :
                i_phaseMask(searcher->GetPhaseMask()), i_object(result), i_check(check)
        {
        }

        void Visit (CreatureMapType &m);
        void Visit (PlayerMapType &m);

        template<class NOT_INTERESTED> void Visit (GridRefManager<NOT_INTERESTED> &)
        {
        }
    };

// All accepted by Check units if any
    template<class Check>
    struct UnitListSearcher
    {
        uint32 i_phaseMask;
        std::list<Unit*> &i_objects;
        Check& i_check;

        UnitListSearcher (WorldObject const* searcher, std::list<Unit*> &objects, Check & check) :
                i_phaseMask(searcher->GetPhaseMask()), i_objects(objects), i_check(check)
        {
        }

        void Visit (PlayerMapType &m);
        void Visit (CreatureMapType &m);

        template<class NOT_INTERESTED> void Visit (GridRefManager<NOT_INTERESTED> &)
        {
        }
    };

// Creature searchers

    template<class Check>
    struct CreatureSearcher
    {
        uint32 i_phaseMask;
        Creature* &i_object;
        Check & i_check;

        CreatureSearcher (WorldObject const* searcher, Creature* & result, Check & check) :
                i_phaseMask(searcher->GetPhaseMask()), i_object(result), i_check(check)
        {
        }

        void Visit (CreatureMapType &m);

        template<class NOT_INTERESTED> void Visit (GridRefManager<NOT_INTERESTED> &)
        {
        }
    };

// Last accepted by Check Creature if any (Check can change requirements at each call)
    template<class Check>
    struct CreatureLastSearcher
    {
        uint32 i_phaseMask;
        Creature* &i_object;
        Check & i_check;

        CreatureLastSearcher (WorldObject const* searcher, Creature* & result, Check & check) :
                i_phaseMask(searcher->GetPhaseMask()), i_object(result), i_check(check)
        {
        }

        void Visit (CreatureMapType &m);

        template<class NOT_INTERESTED> void Visit (GridRefManager<NOT_INTERESTED> &)
        {
        }
    };

    template<class Check>
    struct CreatureListSearcher
    {
        uint32 i_phaseMask;
        std::list<Creature*> &i_objects;
        Check& i_check;

        CreatureListSearcher (WorldObject const* searcher, std::list<Creature*> &objects, Check & check) :
                i_phaseMask(searcher->GetPhaseMask()), i_objects(objects), i_check(check)
        {
        }

        void Visit (CreatureMapType &m);

        template<class NOT_INTERESTED> void Visit (GridRefManager<NOT_INTERESTED> &)
        {
        }
    };

    template<class Do>
    struct CreatureWorker
    {
        uint32 i_phaseMask;
        Do& i_do;

        CreatureWorker (WorldObject const* searcher, Do& _do) :
                i_phaseMask(searcher->GetPhaseMask()), i_do(_do)
        {
        }

        void Visit (CreatureMapType &m)
        {
            for (CreatureMapType::iterator itr = m.begin(); itr != m.end(); ++itr)
                if (itr->getSource()->InSamePhase(i_phaseMask))
                    i_do(itr->getSource());
        }

        template<class NOT_INTERESTED> void Visit (GridRefManager<NOT_INTERESTED> &)
        {
        }
    };

// Player searchers

    template<class Check>
    struct PlayerSearcher
    {
        uint32 i_phaseMask;
        Player* &i_object;
        Check & i_check;

        PlayerSearcher (WorldObject const* searcher, Player* & result, Check & check) :
                i_phaseMask(searcher->GetPhaseMask()), i_object(result), i_check(check)
        {
        }

        void Visit (PlayerMapType &m);

        template<class NOT_INTERESTED> void Visit (GridRefManager<NOT_INTERESTED> &)
        {
        }
    };

    template<class Check>
    struct PlayerListSearcher
    {
        uint32 i_phaseMask;
        std::list<Player*> &i_objects;
        Check& i_check;

        PlayerListSearcher (WorldObject const* searcher, std::list<Player*> &objects, Check & check) :
                i_phaseMask(searcher->GetPhaseMask()), i_objects(objects), i_check(check)
        {
        }

        void Visit (PlayerMapType &m);

        template<class NOT_INTERESTED> void Visit (GridRefManager<NOT_INTERESTED> &)
        {
        }
    };

    template<class Check>
    struct PlayerLastSearcher
    {
        uint32 i_phaseMask;
        Player* &i_object;
        Check& i_check;

        PlayerLastSearcher (WorldObject const* searcher, Player*& result, Check& check) :
                i_phaseMask(searcher->GetPhaseMask()), i_object(result), i_check(check)
        {
        }

        void Visit (PlayerMapType& m);

        template<class NOT_INTERESTED> void Visit (GridRefManager<NOT_INTERESTED> &)
        {
        }
    };

    template<class Do>
    struct PlayerWorker
    {
        uint32 i_phaseMask;
        Do& i_do;

        PlayerWorker (WorldObject const* searcher, Do& _do) :
                i_phaseMask(searcher->GetPhaseMask()), i_do(_do)
        {
        }

        void Visit (PlayerMapType &m)
        {
            for (PlayerMapType::iterator itr = m.begin(); itr != m.end(); ++itr)
                if (itr->getSource()->InSamePhase(i_phaseMask))
                    i_do(itr->getSource());
        }

        template<class NOT_INTERESTED> void Visit (GridRefManager<NOT_INTERESTED> &)
        {
        }
    };

    template<class Do>
    struct PlayerDistWorker
    {
        WorldObject const* i_searcher;
        float i_dist;
        Do& i_do;

        PlayerDistWorker (WorldObject const* searcher, float _dist, Do& _do) :
                i_searcher(searcher), i_dist(_dist), i_do(_do)
        {
        }

        void Visit (PlayerMapType &m)
        {
            for (PlayerMapType::iterator itr = m.begin(); itr != m.end(); ++itr)
                if (itr->getSource()->InSamePhase(i_searcher) && itr->getSource()->IsWithinDist(i_searcher, i_dist))
                    i_do(itr->getSource());
        }

        template<class NOT_INTERESTED> void Visit (GridRefManager<NOT_INTERESTED> &)
        {
        }
    };

// CHECKS && DO classes

// WorldObject check classes
    class RaiseDeadObjectCheck
    {
    public:
        RaiseDeadObjectCheck (Unit* funit, float range) :
                i_funit(funit), i_range(range)
        {
        }
        bool operator() (Creature* u)
        {
            if (i_funit->GetTypeId() != TYPEID_PLAYER || !((Player*) i_funit)->isHonorOrXPTarget(u) || u->getDeathState() != CORPSE || u->isDeadByDefault() || u->isInFlight() || (u->GetCreatureTypeMask() & (1 << (CREATURE_TYPE_HUMANOID - 1))) == 0 || (u->GetDisplayId() != u->GetNativeDisplayId()))
                return false;

            return i_funit->IsWithinDistInMap(u, i_range);
        }
        template<class NOT_INTERESTED> bool operator() (NOT_INTERESTED*)
        {
            return false;
        }
    private:
        Unit* const i_funit;
        float i_range;
    };

    class ExplodeCorpseObjectCheck
    {
    public:
        ExplodeCorpseObjectCheck (Unit* funit, float range) :
                i_funit(funit), i_range(range)
        {
        }
        bool operator() (Player* u)
        {
            if (u->getDeathState() != CORPSE || u->isInFlight() || u->HasAuraType(SPELL_AURA_GHOST) || (u->GetDisplayId() != u->GetNativeDisplayId()))
                return false;

            return i_funit->IsWithinDistInMap(u, i_range);
        }
        bool operator() (Creature* u)
        {
            if (u->getDeathState() != CORPSE || u->isInFlight() || u->isDeadByDefault() || (u->GetDisplayId() != u->GetNativeDisplayId()) || (u->GetCreatureTypeMask() & CREATURE_TYPEMASK_MECHANICAL_OR_ELEMENTAL) != 0)
                return false;

            return i_funit->IsWithinDistInMap(u, i_range);
        }
        template<class NOT_INTERESTED> bool operator() (NOT_INTERESTED*)
        {
            return false;
        }
    private:
        Unit* const i_funit;
        float i_range;
    };

    class CannibalizeObjectCheck
    {
    public:
        CannibalizeObjectCheck (Unit* funit, float range) :
                i_funit(funit), i_range(range)
        {
        }
        bool operator() (Player* u)
        {
            if (i_funit->IsFriendlyTo(u) || u->isAlive() || u->isInFlight())
                return false;

            return i_funit->IsWithinDistInMap(u, i_range);
        }
        bool operator() (Corpse* u);
        bool operator() (Creature* u)
        {
            if (i_funit->IsFriendlyTo(u) || u->isAlive() || u->isInFlight() || (u->GetCreatureTypeMask() & CREATURE_TYPEMASK_HUMANOID_OR_UNDEAD) == 0)
                return false;

            return i_funit->IsWithinDistInMap(u, i_range);
        }
        template<class NOT_INTERESTED> bool operator() (NOT_INTERESTED*)
        {
            return false;
        }
    private:
        Unit* const i_funit;
        float i_range;
    };

    class CarrionFeederObjectCheck
    {
    public:
        CarrionFeederObjectCheck (Unit* funit, float range) :
                i_funit(funit), i_range(range)
        {
        }
        bool operator() (Player* u)
        {
            if (i_funit->IsFriendlyTo(u) || u->isAlive() || u->isInFlight())
                return false;

            return i_funit->IsWithinDistInMap(u, i_range);
        }
        bool operator() (Corpse* u);
        bool operator() (Creature* u)
        {
            if (i_funit->IsFriendlyTo(u) || u->isAlive() || u->isInFlight() || (u->GetCreatureTypeMask() & CREATURE_TYPEMASK_MECHANICAL_OR_ELEMENTAL) != 0)
                return false;

            return i_funit->IsWithinDistInMap(u, i_range);
        }
        template<class NOT_INTERESTED> bool operator() (NOT_INTERESTED*)
        {
            return false;
        }
    private:
        Unit* const i_funit;
        float i_range;
    };

// WorldObject do classes

    class RespawnDo
    {
    public:
        RespawnDo ()
        {
        }
        void operator() (Creature* u) const
        {
            u->Respawn();
        }
        void operator() (GameObject* u) const
        {
            u->Respawn();
        }
        void operator() (WorldObject*) const
        {
        }
        void operator() (Corpse*) const
        {
        }
    };

// GameObject checks

    class GameObjectFocusCheck
    {
    public:
        GameObjectFocusCheck (Unit const* unit, uint32 focusId) :
                i_unit(unit), i_focusId(focusId)
        {
        }
        bool operator() (GameObject* go) const
        {
            if (go->GetGOInfo()->type != GAMEOBJECT_TYPE_SPELL_FOCUS)
                return false;

            if (go->GetGOInfo()->spellFocus.focusId != i_focusId)
                return false;

            float dist = (float) ((go->GetGOInfo()->spellFocus.dist) / 2);

            return go->IsWithinDistInMap(i_unit, dist);
        }
    private:
        Unit const* i_unit;
        uint32 i_focusId;
    };

// Find the nearest Fishing hole and return true only if source object is in range of hole
    class NearestGameObjectFishingHole
    {
    public:
        NearestGameObjectFishingHole (WorldObject const& obj, float range) :
                i_obj(obj), i_range(range)
        {
        }
        bool operator() (GameObject* go)
        {
            if (go->GetGOInfo()->type == GAMEOBJECT_TYPE_FISHINGHOLE && go->isSpawned() && i_obj.IsWithinDistInMap(go, i_range) && i_obj.IsWithinDistInMap(go, (float) go->GetGOInfo()->fishinghole.radius))
            {
                i_range = i_obj.GetDistance(go);
                return true;
            }
            return false;
        }
        float GetLastRange () const
        {
            return i_range;
        }
    private:
        WorldObject const& i_obj;
        float i_range;

        // prevent clone
        NearestGameObjectFishingHole (NearestGameObjectFishingHole const&);
    };

    class NearestGameObjectCheck
    {
    public:
        NearestGameObjectCheck (WorldObject const& obj) :
                i_obj(obj), i_range(999)
        {
        }
        bool operator() (GameObject* go)
        {
            if (i_obj.IsWithinDistInMap(go, i_range))
            {
                i_range = i_obj.GetDistance(go);          // use found GO range as new range limit for next check
                return true;
            }
            return false;
        }
        float GetLastRange () const
        {
            return i_range;
        }
    private:
        WorldObject const& i_obj;
        float i_range;

        // prevent clone this object
        NearestGameObjectCheck (NearestGameObjectCheck const&);
    };

// Success at unit in range, range update for next check (this can be use with GameobjectLastSearcher to find nearest GO)
    class NearestGameObjectEntryInObjectRangeCheck
    {
    public:
        NearestGameObjectEntryInObjectRangeCheck (WorldObject const& obj, uint32 entry, float range) :
                i_obj(obj), i_entry(entry), i_range(range)
        {
        }
        bool operator() (GameObject* go)
        {
            if (go->GetEntry() == i_entry && i_obj.IsWithinDistInMap(go, i_range))
            {
                i_range = i_obj.GetDistance(go);          // use found GO range as new range limit for next check
                return true;
            }
            return false;
        }
        float GetLastRange () const
        {
            return i_range;
        }
    private:
        WorldObject const& i_obj;
        uint32 i_entry;
        float i_range;

        // prevent clone this object
        NearestGameObjectEntryInObjectRangeCheck (NearestGameObjectEntryInObjectRangeCheck const&);
    };

    class GameObjectWithDbGUIDCheck
    {
    public:
        GameObjectWithDbGUIDCheck (WorldObject const& obj, uint32 db_guid) :
                i_obj(obj), i_db_guid(db_guid)
        {
        }
        bool operator() (GameObject const* go) const
        {
            return go->GetDBTableGUIDLow() == i_db_guid;
        }
    private:
        WorldObject const& i_obj;
        uint32 i_db_guid;
    };

// Unit checks

    class MostHPMissingInRange
    {
    public:
        MostHPMissingInRange (Unit const* obj, float range, uint32 hp) :
                i_obj(obj), i_range(range), i_hp(hp)
        {
        }
        bool operator() (Unit* u)
        {
            if (u->isAlive() && u->isInCombat() && !i_obj->IsHostileTo(u) && i_obj->IsWithinDistInMap(u, i_range) && u->GetMaxHealth() - u->GetHealth() > i_hp)
            {
                i_hp = u->GetMaxHealth() - u->GetHealth();
                return true;
            }
            return false;
        }
    private:
        Unit const* i_obj;
        float i_range;
        uint32 i_hp;
    };

    class FriendlyCCedInRange
    {
    public:
        FriendlyCCedInRange (Unit const* obj, float range) :
                i_obj(obj), i_range(range)
        {
        }
        bool operator() (Unit* u)
        {
            if (u->isAlive() && u->isInCombat() && !i_obj->IsHostileTo(u) && i_obj->IsWithinDistInMap(u, i_range) && (u->isFeared() || u->isCharmed() || u->isFrozen() || u->HasUnitState(UNIT_STAT_STUNNED) || u->HasUnitState(UNIT_STAT_CONFUSED)))
            {
                return true;
            }
            return false;
        }
    private:
        Unit const* i_obj;
        float i_range;
    };

    class FriendlyMissingBuffInRange
    {
    public:
        FriendlyMissingBuffInRange (Unit const* obj, float range, uint32 spellid) :
                i_obj(obj), i_range(range), i_spell(spellid)
        {
        }
        bool operator() (Unit* u)
        {
            if (u->isAlive() && u->isInCombat() && !i_obj->IsHostileTo(u) && i_obj->IsWithinDistInMap(u, i_range) && !(u->HasAura(i_spell)))
            {
                return true;
            }
            return false;
        }
    private:
        Unit const* i_obj;
        float i_range;
        uint32 i_spell;
    };

    class AnyUnfriendlyUnitInObjectRangeCheck
    {
    public:
        AnyUnfriendlyUnitInObjectRangeCheck (WorldObject const* obj, Unit const* funit, float range) :
                i_obj(obj), i_funit(funit), i_range(range)
        {
        }
        bool operator() (Unit* u)
        {
            if (u->isAlive() && i_obj->IsWithinDistInMap(u, i_range) && !i_funit->IsFriendlyTo(u))
                return true;
            else
                return false;
        }
    private:
        WorldObject const* i_obj;
        Unit const* i_funit;
        float i_range;
    };

    class AnyUnfriendlyNoTotemUnitInObjectRangeCheck
    {
    public:
        AnyUnfriendlyNoTotemUnitInObjectRangeCheck (WorldObject const* obj, Unit const* funit, float range) :
                i_obj(obj), i_funit(funit), i_range(range)
        {
        }
        bool operator() (Unit* u)
        {
            if (!u->isAlive())
                return false;

            if (u->GetCreatureType() == CREATURE_TYPE_NON_COMBAT_PET)
                return false;

            if (u->GetTypeId() == TYPEID_UNIT && ((Creature*) u)->isTotem())
                return false;

            if (!u->isTargetableForAttack(false))
                return false;

            return i_obj->IsWithinDistInMap(u, i_range) && !i_funit->IsFriendlyTo(u);
        }
    private:
        WorldObject const* i_obj;
        Unit const* i_funit;
        float i_range;
    };

    class AnyUnfriendlyVisibleUnitInObjectRangeCheck
    {
    public:
        AnyUnfriendlyVisibleUnitInObjectRangeCheck (WorldObject const* obj, Unit const* funit, float range) :
                i_obj(obj), i_funit(funit), i_range(range)
        {
        }

        bool operator() (Unit* u)
        {
            return u->isAlive() && i_obj->IsWithinDistInMap(u, i_range) && !i_funit->IsFriendlyTo(u) && i_funit->canSeeOrDetect(u);
        }
    private:
        WorldObject const* i_obj;
        Unit const* i_funit;
        float i_range;
    };

    class CreatureWithDbGUIDCheck
    {
    public:
        CreatureWithDbGUIDCheck (WorldObject const* obj, uint32 lowguid) :
                i_obj(obj), i_lowguid(lowguid)
        {
        }
        bool operator() (Creature* u)
        {
            return u->GetDBTableGUIDLow() == i_lowguid;
        }
    private:
        WorldObject const* i_obj;
        uint32 i_lowguid;
    };

    class AnyFriendlyUnitInObjectRangeCheck
    {
    public:
        AnyFriendlyUnitInObjectRangeCheck (WorldObject const* obj, Unit const* funit, float range) :
                i_obj(obj), i_funit(funit), i_range(range)
        {
        }
        bool operator() (Unit* u)
        {
            if (u->isAlive() && i_obj->IsWithinDistInMap(u, i_range) && i_funit->IsFriendlyTo(u))
                return true;
            else
                return false;
        }
    private:
        WorldObject const* i_obj;
        Unit const* i_funit;
        float i_range;
    };

    class AnyUnitInObjectRangeCheck
    {
    public:
        AnyUnitInObjectRangeCheck (WorldObject const* obj, float range) :
                i_obj(obj), i_range(range)
        {
        }
        bool operator() (Unit* u)
        {
            if (u->isAlive() && i_obj->IsWithinDistInMap(u, i_range))
                return true;

            return false;
        }
    private:
        WorldObject const* i_obj;
        float i_range;
    };

// Success at unit in range, range update for next check (this can be use with UnitLastSearcher to find nearest unit)
    class NearestAttackableUnitInObjectRangeCheck
    {
    public:
        NearestAttackableUnitInObjectRangeCheck (WorldObject const* obj, Unit const* funit, float range) :
                i_obj(obj), i_funit(funit), i_range(range)
        {
        }
        bool operator() (Unit* u)
        {
            if (u->isTargetableForAttack() && i_obj->IsWithinDistInMap(u, i_range) && !i_funit->IsFriendlyTo(u) && i_funit->canSeeOrDetect(u))
            {
                i_range = i_obj->GetDistance(u);          // use found unit range as new range limit for next check
                return true;
            }

            return false;
        }
    private:
        WorldObject const* i_obj;
        Unit const* i_funit;
        float i_range;

        // prevent clone this object
        NearestAttackableUnitInObjectRangeCheck (NearestAttackableUnitInObjectRangeCheck const&);
    };

    class AnyAoETargetUnitInObjectRangeCheck
    {
    public:
        AnyAoETargetUnitInObjectRangeCheck (WorldObject const* obj, Unit const* funit, float range) :
                i_obj(obj), i_funit(funit), i_range(range)
        {
            Unit const* check = i_funit;
            Unit const* owner = i_funit->GetOwner();
            if (owner)
                check = owner;
            i_targetForPlayer = (check->GetTypeId() == TYPEID_PLAYER);
        }
        bool operator() (Unit* u)
        {
            // Check contains checks for: live, non-selectable, non-attackable flags, flight check and GM check, ignore totems
            if (!u->isTargetableForAttack())
                return false;
            if (u->GetTypeId() == TYPEID_UNIT && ((Creature*) u)->isTotem())
                return false;

            if ((i_targetForPlayer ? !i_funit->IsFriendlyTo(u) : i_funit->IsHostileTo(u)) && i_obj->IsWithinDistInMap(u, i_range))
                return true;

            return false;
        }
    private:
        bool i_targetForPlayer;
        WorldObject const* i_obj;
        Unit const* i_funit;
        float i_range;
    };

// do attack at call of help to friendly crearture
    class CallOfHelpCreatureInRangeDo
    {
    public:
        CallOfHelpCreatureInRangeDo (Unit* funit, Unit* enemy, float range) :
                i_funit(funit), i_enemy(enemy), i_range(range)
        {
        }
        void operator() (Creature* u)
        {
            if (u == i_funit)
                return;

            if (!u->CanAssistTo(i_funit, i_enemy, false))
                return;

            // too far
            if (!u->IsWithinDistInMap(i_enemy, i_range))
                return;

            // only if see assisted creature's enemy
            if (!u->IsWithinLOSInMap(i_enemy))
                return;

            if (u->AI())
                u->AI()->AttackStart(i_enemy);
        }
    private:
        Unit* const i_funit;
        Unit* const i_enemy;
        float i_range;
    };

    struct AnyDeadUnitCheck
    {
        bool operator() (Unit* u)
        {
            return !u->isAlive();
        }
    };

//struct AnyStealthedCheck
//{
//    bool operator()(Unit* u) { return u->GetVisibility() == VISIBILITY_GROUP_STEALTH; }
//};

// Creature checks

    class NearestHostileUnitCheck
    {
    public:
        explicit NearestHostileUnitCheck (Creature const* creature, float dist = 0) :
                me(creature)
        {
            m_range = (dist == 0 ? 9999 : dist);
        }
        bool operator() (Unit* u)
        {
            if (!me->IsWithinDistInMap(u, m_range))
                return false;

            if (!me->canSeeOrDetect(u))
                return false;

            if (!me->canAttack(u))
                return false;

            m_range = me->GetDistance(u);          // use found unit range as new range limit for next check
            return true;
        }

    private:
        Creature const *me;
        float m_range;
        NearestHostileUnitCheck (NearestHostileUnitCheck const&);
    };

    class NearestHostileUnitInAttackDistanceCheck
    {
    public:
        explicit NearestHostileUnitInAttackDistanceCheck (Creature const* creature, float dist = 0) :
                me(creature)
        {
            m_range = (dist == 0 ? 9999 : dist);
            m_force = (dist == 0 ? false : true);
        }
        bool operator() (Unit* u)
        {
            if (!me->IsWithinDistInMap(u, m_range))
                return false;

            if (m_force)
            {
                if (!me->canAttack(u))
                    return false;
            }
            else if (!me->canStartAttack(u, false))
                return false;

            m_range = me->GetDistance(u);          // use found unit range as new range limit for next check
            return true;
        }
        float GetLastRange () const
        {
            return m_range;
        }
    private:
        Creature const *me;
        float m_range;
        bool m_force;
        NearestHostileUnitInAttackDistanceCheck (NearestHostileUnitInAttackDistanceCheck const&);
    };

    class AnyAssistCreatureInRangeCheck
    {
    public:
        AnyAssistCreatureInRangeCheck (Unit* funit, Unit* enemy, float range) :
                i_funit(funit), i_enemy(enemy), i_range(range)
        {
        }
        bool operator() (Creature* u)
        {
            if (u == i_funit)
                return false;

            if (!u->CanAssistTo(i_funit, i_enemy))
                return false;

            // too far
            if (!i_funit->IsWithinDistInMap(u, i_range))
                return false;

            // only if see assisted creature
            if (!i_funit->IsWithinLOSInMap(u))
                return false;

            return true;
        }
    private:
        Unit* const i_funit;
        Unit* const i_enemy;
        float i_range;
    };

    class NearestAssistCreatureInCreatureRangeCheck
    {
    public:
        NearestAssistCreatureInCreatureRangeCheck (Creature* obj, Unit* enemy, float range) :
                i_obj(obj), i_enemy(enemy), i_range(range)
        {
        }

        bool operator() (Creature* u)
        {
            if (u == i_obj)
                return false;
            if (!u->CanAssistTo(i_obj, i_enemy))
                return false;

            if (!i_obj->IsWithinDistInMap(u, i_range))
                return false;

            if (!i_obj->IsWithinLOSInMap(u))
                return false;

            i_range = i_obj->GetDistance(u);          // use found unit range as new range limit for next check
            return true;
        }
        float GetLastRange () const
        {
            return i_range;
        }
    private:
        Creature* const i_obj;
        Unit* const i_enemy;
        float i_range;

        // prevent clone this object
        NearestAssistCreatureInCreatureRangeCheck (NearestAssistCreatureInCreatureRangeCheck const&);
    };

// Success at unit in range, range update for next check (this can be use with CreatureLastSearcher to find nearest creature)
    class NearestCreatureEntryWithLiveStateInObjectRangeCheck
    {
    public:
        NearestCreatureEntryWithLiveStateInObjectRangeCheck (WorldObject const& obj, uint32 entry, bool alive, float range) :
                i_obj(obj), i_entry(entry), i_alive(alive), i_range(range)
        {
        }

        bool operator() (Creature* u)
        {
            if (u->GetEntry() == i_entry && u->isAlive() == i_alive && i_obj.IsWithinDistInMap(u, i_range))
            {
                i_range = i_obj.GetDistance(u);          // use found unit range as new range limit for next check
                return true;
            }
            return false;
        }
        float GetLastRange () const
        {
            return i_range;
        }
    private:
        WorldObject const& i_obj;
        uint32 i_entry;
        bool i_alive;
        float i_range;

        // prevent clone this object
        NearestCreatureEntryWithLiveStateInObjectRangeCheck (NearestCreatureEntryWithLiveStateInObjectRangeCheck const&);
    };

    class AnyPlayerInObjectRangeCheck
    {
    public:
        AnyPlayerInObjectRangeCheck (WorldObject const* obj, float range, bool reqAlive = true) :
                _obj(obj), _range(range), _reqAlive(reqAlive)
        {
        }
        bool operator() (Player* u)
        {
            if (_reqAlive && !u->isAlive())
                return false;

            if (!_obj->IsWithinDistInMap(u, _range))
                return false;

            return true;
        }

    private:
        WorldObject const* _obj;
        float _range;
        bool _reqAlive;
    };

    class NearestPlayerInObjectRangeCheck
    {
    public:
        NearestPlayerInObjectRangeCheck (WorldObject const* obj, float range) :
                i_obj(obj), i_range(range)
        {
        }

        bool operator() (Player* u)
        {
            if (u->isAlive() && i_obj->IsWithinDistInMap(u, i_range))
            {
                i_range = i_obj->GetDistance(u);
                return true;
            }

            return false;
        }
    private:
        WorldObject const* i_obj;
        float i_range;

        NearestPlayerInObjectRangeCheck (NearestPlayerInObjectRangeCheck const&);
    };

    class AllFriendlyCreaturesInGrid
    {
    public:
        AllFriendlyCreaturesInGrid (Unit const* obj) :
                pUnit(obj)
        {
        }
        bool operator() (Unit* u)
        {
            if (u->isAlive() && u->IsVisible() && u->IsFriendlyTo(pUnit))
                return true;

            return false;
        }
    private:
        Unit const* pUnit;
    };

    class AllGameObjectsWithEntryInRange
    {
    public:
        AllGameObjectsWithEntryInRange (const WorldObject* pObject, uint32 uiEntry, float fMaxRange) :
                m_pObject(pObject), m_uiEntry(uiEntry), m_fRange(fMaxRange)
        {
        }
        bool operator() (GameObject* pGo)
        {
            if (pGo->GetEntry() == m_uiEntry && m_pObject->IsWithinDist(pGo, m_fRange, false))
                return true;

            return false;
        }
    private:
        const WorldObject* m_pObject;
        uint32 m_uiEntry;
        float m_fRange;
    };

    class AllCreaturesOfEntryInRange
    {
    public:
        AllCreaturesOfEntryInRange (const WorldObject* pObject, uint32 uiEntry, float fMaxRange) :
                m_pObject(pObject), m_uiEntry(uiEntry), m_fRange(fMaxRange)
        {
        }
        bool operator() (Unit* pUnit)
        {
            if (pUnit->GetEntry() == m_uiEntry && m_pObject->IsWithinDist(pUnit, m_fRange, false))
                return true;

            return false;
        }

    private:
        const WorldObject* m_pObject;
        uint32 m_uiEntry;
        float m_fRange;
    };

    class AllCreaturesOfEntryInGrid
    {
    public:
        AllCreaturesOfEntryInGrid (const WorldObject* pObject, uint32 uiEntry) :
                m_pObject(pObject), m_uiEntry(uiEntry)
        {
        }
        bool operator() (Unit* pUnit)
        {
            if (pUnit->GetEntry() == m_uiEntry)
                return true;
            return false;
        }
    private:
        const WorldObject* m_pObject;
        uint32 m_uiEntry;
    };

    class PlayerAtMinimumRangeAway
    {
    public:
        PlayerAtMinimumRangeAway (Unit const* unit, float fMinRange) :
                pUnit(unit), fRange(fMinRange)
        {
        }
        bool operator() (Player* pPlayer)
        {
            //No threat list check, must be done explicit if expected to be in combat with creature
            if (!pPlayer->isGameMaster() && pPlayer->isAlive() && !pUnit->IsWithinDist(pPlayer, fRange, false))
                return true;

            return false;
        }

    private:
        Unit const* pUnit;
        float fRange;
    };

    class GameObjectInRangeCheck
    {
    public:
        GameObjectInRangeCheck (float _x, float _y, float _z, float _range, uint32 _entry = 0) :
                x(_x), y(_y), z(_z), range(_range), entry(_entry)
        {
        }
        bool operator() (GameObject* go)
        {
            if (!entry || (go->GetGOInfo() && go->GetGOInfo()->id == entry))
                return go->IsInRange(x, y, z, range);
            else
                return false;
        }
    private:
        float x, y, z, range;
        uint32 entry;
    };

    class AllWorldObjectsInRange
    {
    public:
        AllWorldObjectsInRange (const WorldObject* pObject, float fMaxRange) :
                m_pObject(pObject), m_fRange(fMaxRange)
        {
        }
        bool operator() (WorldObject* pGo)
        {
            return m_pObject->IsWithinDist(pGo, m_fRange, false);
        }
    private:
        const WorldObject* m_pObject;
        float m_fRange;
    };

    class ObjectTypeIdCheck
    {
    public:
        ObjectTypeIdCheck (TypeID typeId, bool equals) :
                _typeId(typeId), _equals(equals)
        {
        }
        bool operator() (WorldObject* object)
        {
            return (object->GetTypeId() == _typeId) == _equals;
        }

    private:
        TypeID _typeId;
        bool _equals;
    };

    class ObjectGUIDCheck
    {
    public:
        ObjectGUIDCheck (uint64 GUID) :
                _GUID(GUID)
        {
        }
        bool operator() (WorldObject* object)
        {
            return object->GetGUID() == _GUID;
        }

    private:
        uint64 _GUID;
    };

    class UnitAuraCheck
    {
    public:
        UnitAuraCheck (bool present, uint32 spellId, uint64 casterGUID = 0) :
                _present(present), _spellId(spellId), _casterGUID(casterGUID)
        {
        }
        bool operator() (Unit* unit)
        {
            return unit->HasAura(_spellId, _casterGUID) == _present;
        }

    private:
        bool _present;
        uint32 _spellId;
        uint64 _casterGUID;
    };

// Player checks and do

// Prepare using Builder localized packets with caching and send to player
    template<class Builder>
    class LocalizedPacketDo
    {
    public:
        explicit LocalizedPacketDo (Builder& builder) :
                i_builder(builder)
        {
        }

        ~LocalizedPacketDo ()
        {
            for (size_t i = 0; i < i_data_cache.size(); ++i)
                delete i_data_cache[i];
        }
        void operator() (Player* p);

    private:
        Builder& i_builder;
        std::vector<WorldPacket*> i_data_cache;          // 0 = default, i => i-1 locale index
    };

// Prepare using Builder localized packets with caching and send to player
    template<class Builder>
    class LocalizedPacketListDo
    {
    public:
        typedef std::vector<WorldPacket*> WorldPacketList;
        explicit LocalizedPacketListDo (Builder& builder) :
                i_builder(builder)
        {
        }

        ~LocalizedPacketListDo ()
        {
            for (size_t i = 0; i < i_data_cache.size(); ++i)
                for (size_t j = 0; j < i_data_cache[i].size(); ++j)
                    delete i_data_cache[i][j];
        }
        void operator() (Player* p);

    private:
        Builder& i_builder;
        std::vector<WorldPacketList> i_data_cache;
        // 0 = default, i => i-1 locale index
    };
}
#endif
